
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include"task.hpp"

#define PROCESS_NUM 5 //子进程数目
using namespace std;

//子进程使用：用于等待接受父进程传递的任务编号指令，以便后续执行
int waitCommand(int pipefd, bool& quit)
{
    //读取：从对应的pipefd中读取到相关的指令，将其返回。
    uint32_t command = 0;
    int ret = read(pipefd, &command, sizeof(command));
    if(ret==0)//读取到文件末尾
    {
        quit=true;
        return -1;
    }
    assert(ret==sizeof(uint32_t));//用于检测读取到的指令是否合法
    return command;
}


//父进程使用：用于委派任务给子进程
void sentAndWakeup(pid_t id, int pipefd, uint32_t command)
{
    //写入：将对应的command传递到子进程对应的通信管道中
    write(pipefd,&command,sizeof(command));
    cout<< "【father process-" << getpid() <<"】: call child process-" << id << ", execute task-" << desc[command] << ", child pipefd-" << pipefd <<"\n" <<endl;
}

int main()
{
    //1、准备工作
    Load();//加载任务
    vector<pair<pid_t,int>> slots;//用于记录创建出的子进程，以及该进程对应的通信管道


    //2、创建子进程，建立单向通信管道
    for(int i = 0; i < PROCESS_NUM; ++i)
    {
        //建立管道
        int pipefd[2];
        int n = pipe(pipefd);
        assert(n == 0);
        (void)n;

        //创建子进程
        pid_t id = fork();
        assert(id!=-1);

        if(0==id)//对子进程
        {
            close(pipefd[1]);

            //3、子进程开始工作
            while(true)
            {
                // 等待父进程命令
                bool quit = false;
                int command = waitCommand(pipefd[0], quit);
                if(quit)//在管道中读取到文件尾
                    break;

                // 执行对应的命令：此处对command做了检测
                if(command >= 0 && command <handlerSize())
                {
                    calltasks[command];
                }
                else{
                    cout<<"command error: "<< command << endl;
                }
            }
            exit(1);//子进程结束，退出
        }
        //2、对父进程
        close(pipefd[0]);//对管道:关闭读端
        slots.push_back({id,pipefd[1]});//对进程:记录当前创建的子进程id及其通信管道fd
    }

    //3、父进程开始工作:派遣任务
    srand((unsigned long)time(nullptr)*getpid());//随机数方式的负载均衡，这里为了尽量随机可做一些处理
    while(true)
    {

        int command = rand() % handlerSize();// 选择一个任务:获取任务编号
        int choosechild = rand() % slots.size();// 选择一个子进程:获取到子进程id,通信管道fd
        sentAndWakeup(slots[choosechild].first, slots[choosechild].second, command); // 将任务派遣给子进程:根据选择的进程choosechild，将command任务编号传入该进程对应的通信管道。

        sleep(1);
    }

    //4、父进程结束工作：收尾处理
    for(const auto &it : slots)//关闭所有通信管道
    {
        close(it.second);
    }

    for(const auto &it : slots)//回收所有的子进程信息
    {
        waitpid(it.first,nullptr,0);
    }

    return 0;
}


